<?php

/**
 * SimplifyPHP Framework
 *
 * This file is part of SimplifyPHP Framework.
 *
 * SimplifyPHP Framework is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SimplifyPHP Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 */

require_once ('api/IQueryObject.php');

/**
 *
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 * @package Simplify_Kernel_DAO
 */
abstract class QueryObject implements IQueryObject
{

  const SELECT = 1;

  const UPDATE = 2;

  const INSERT = 4;

  const DELETE = 8;

  const ALL = 15;

  const JOIN_INNER = 'INNER JOIN';

  const JOIN_LEFT = 'LEFT JOIN';

  const JOIN_RIGHT = 'RIGHT JOIN';

  const ORDER_ASC = 'ASC';

  const ORDER_DESC = 'DESC';

  protected $query = 1;

  protected $fields = array();

  protected $table = array();

  protected $joins = array();

  protected $groupBy = array();

  protected $having = array();

  protected $where = array();

  protected $orderBy = array();

  protected $limit;

  protected $offset;

  protected $data;

  protected $sql;

  protected $__sql = null;

  /**
   *
   * @var IDataAccessObject
   */
  protected $dao;

  /**
   *
   * @param IDataAccessObject $dao
   * @return void
   */
  public function __construct($dao)
  {
    $this->dao = $dao;
  }

  /**
   *
   * @return QueryObject
   */
  public function insert($table = null, $data = null)
  {
    $this->query = QueryObject::INSERT;

    $this->from($table);
    $this->data($data);

    return $this;
  }

  /**
   *
   * @return QueryObject
   */
  public function update($table = null, $data = null, $where = null)
  {
    $this->query = QueryObject::UPDATE;

    $this->from($table);
    $this->data($data);
    $this->where($where);

    return $this;
  }

  /**
   *
   * @return QueryObject
   */
  public function delete($table = null, $where = null)
  {
    $this->query = QueryObject::DELETE;

    $this->from($table);
    $this->where($where);

    return $this;
  }

  public function data($data = null, $remove = null)
  {
    if ($data === true) {
      return $this->data;
    }
    elseif ($data === false) {
      $this->data = null;
    }
    else {
      $this->data = $data;

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#select($fields)
   */
  public function select($fields = null, $remove = null)
  {
    if ($fields === true) {
      return $this->fields;
    }
    elseif ($fields === false) {
      $this->fields = array();
    }
    elseif (! empty($fields)) {
      $this->query = QueryObject::SELECT;

      $fields = (array) $fields;

      if ($remove) {
        $this->fields = array_diff($this->fields, $fields);
      }
      else {
        $this->fields = array_unique(array_merge($this->fields, $fields));
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#from($table)
   */
  public function from($table = null, $remove = null)
  {
    if ($table === true) {
      return $this->table;
    }
    elseif ($table === false) {
      $this->table = array();
    }
    elseif (! empty($table)) {
      $table = (array) $table;

      if ($remove) {
        $this->table = array_diff($this->table, $table);
      }
      else {
        $this->table = array_unique(array_merge($this->table, $table));
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#join($table, $condition, $type)
   */
  public function join($join = null, $type = null, $remove = null)
  {
    if ($join === true) {
      return $this->joins;
    }
    elseif ($join === false) {
      $this->joins = array();
    }
    elseif (! empty($join)) {
      if (empty($type)) {
        $type = QueryObject::JOIN_INNER;
      }

      $join = array($type, $join);

      if ($remove) {
        if (($i = array_search($join, $thi->joins)) !== null) {
          unset($this->joins[$i]);
        }
      }
      else {
        $this->joins[] = $join;
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#join($table, $condition, $type)
   */
  public function leftJoin($join = null, $remove = null)
  {
    return $this->join($join, self::JOIN_LEFT, $remove);
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#join($table, $condition, $type)
   */
  public function rightJoin($join = null, $remove = null)
  {
    return $this->join($join, self::JOIN_RIGHT, $remove);
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#join($table, $condition, $type)
   */
  public function innerJoin($join = null, $remove = null)
  {
    return $this->join($join, self::JOIN_INNER, $remove);
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#groupBy($s)
   */
  public function groupBy($field = null, $remove = null)
  {
    if ($field === true) {
      return $this->groupBy;
    }
    elseif ($field === false) {
      $this->groupBy = array();
    }
    elseif (! empty($field)) {
      $field = (array) $field;

      if ($remove) {
        $this->groupBy = array_diff($this->groupBy, $field);
      }
      else {
        $this->groupBy = array_unique(array_merge($this->groupBy, $field));
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#having($s)
   */
  public function having($condition = null, $remove = null)
  {
    if ($condition === true) {
      return $this->having;
    }
    elseif ($condition === false) {
      $this->having = array();
    }
    elseif (! empty($condition)) {
      $condition = (array) $condition;

      if ($remove) {
        $this->having = array_diff($this->having, $condition);
      }
      else {
        $this->having = array_unique(array_merge($this->having, $condition));
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#where($s)
   */
  public function where($condition = null, $remove = null)
  {
    if ($condition === true) {
      return $this->where;
    }
    elseif ($condition === false) {
      $this->where = array();
    }
    elseif (! empty($condition)) {
      $condition = (array) $condition;

      if ($remove) {
        $this->where = array_diff($this->where, $condition);
      }
      else {
        $this->where = array_unique(array_merge($this->where, $condition));
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#orderBy($s, $direction)
   */
  public function orderBy($field = null, $direction = null, $remove = null)
  {
    if ($field === true) {
      return $this->orderBy;
    }
    elseif ($field === false) {
      $this->orderBy = array();
    }
    elseif (! empty($field)) {
      if ($remove) {
        if (isset($this->orderBy[$field])) {
          unset($this->orderBy[$field]);
        }
      }
      else {
        $this->orderBy[$field] = $direction;
      }

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#limit($limit)
   */
  public function limit($limit = null)
  {
    if ($limit === true) {
      return $this->limit;
    }
    elseif ($limit === false) {
      $this->limit = null;
    }
    else {
      $this->limit = intval($limit);

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#offset($offset)
   */
  public function offset($offset = null)
  {
    if ($offset === true) {
      return $this->offset;
    }
    elseif ($offset === false) {
      $this->offset = null;
    }
    else {
      $this->offset = intval($offset);

      $this->__sql = null;
    }

    return $this;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/dao/api/IQueryObject#sql($sql)
   */
  public function sql($sql = null)
  {
    if ($sql === true) {
      return $this->sql;
    }
    elseif ($sql === false) {
      $this->sql = null;
    }
    else {
      $this->sql = $sql;

      $this->__sql = null;
    }

    return $this;
  }

  public function setParams($params = null)
  {
    if (! empty($params)) {
      $params = SimpleDictionary::parseFrom($params)->getAll();

      if (isset($params['sql'])) {
        $this->sql($params['sql']);
      }
      else {
        if (isset($params['fields'])) {
          call_user_func(array($this, 'select'), $params['fields']);
        }

        if (isset($params['select'])) {
          call_user_func(array($this, 'select'), $params['select']);
        }

        if (isset($params['from'])) {
          call_user_func(array($this, 'from'), $params['from']);
        }

        if (isset($params['table'])) {
          call_user_func(array($this, 'from'), $params['table']);
        }

        if (isset($params['joins'])) {
          call_user_func(array($this, 'joins'), $params['joins']);
        }

        if (isset($params['groupBy'])) {
          call_user_func(array($this, 'groupBy'), $params['groupBy']);
        }

        if (isset($params['having'])) {
          call_user_func(array($this, 'having'), $params['having']);
        }

        if (isset($params['where'])) {
          call_user_func(array($this, 'where'), $params['where']);
        }

        if (isset($params['orderBy'])) {
          call_user_func_array(array($this, 'orderBy'), (array)$params['orderBy']);
        }

        if (isset($params['limit'])) {
          $this->limit($params['limit']);
        }

        if (isset($params['offset'])) {
          $this->limit($params['offset']);
        }
      }
    }

    return $this;
  }

  public static function buildInsert($data, $wildcard = null)
  {
    $fields = array();
    $values = array();

    foreach ($data as $k => $v) {
      $fields[] = $k;

      if ($wildcard === false) {
        $values[] = DB::getInstance()->quote($v);
      } elseif (is_string($wildcard)) {
        $values[] = $wildcard;
      } else {
        $values[] = ':' . $k;
      }
    }

    return ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ') ';
  }

  public static function buildUpdate($data, $wildcard = null)
  {
    $fields = array();

    foreach ($data as $k => $v) {
      if ($wildcard === false) {
        $fields[] = $k . ' = ' . DB::getInstance()->quote($v);
      } elseif (is_string($wildcard)) {
        $fields[] = $k . ' = ' . $wildcard;
      } else {
        $fields[] = $k . ' = :' . $k;
      }
    }

    return ' ' . implode(', ', $fields) . ' ';
  }

  /**
   *
   * @return boolean
   */
  protected function accept($query)
  {
    return ($query & $this->query) == $this->query;
  }

}

?>